package com.hqd.jjvm.rtda.obj;

import com.hqd.jjvm.rtda.JThread;
import com.hqd.jjvm.rtda.heap.JClass;
import com.hqd.jjvm.rtda.heap.classloader.JClassLoader;
import com.hqd.jjvm.rtda.slot.Slot;
import lombok.Getter;
import lombok.Setter;

import java.io.IOException;

/**
 * 实例对象
 */
@Getter
@Setter
public class JObject implements Cloneable {
    /**
     * 类信息
     */
    private JClass jClass;
    /**
     * 实例数据
     */
    private Object data;

    private Object extra;
    /**
     * 分代年龄
     */
    private int age;
    /**
     * hash码
     */
    private int hashCode;
    /**
     * 是否偏向锁
     */
    private boolean isBiasedLock;

    private int lock;
    private JThread jThread;


    public JObject(JClass jClass) {
        this.jClass = jClass;
        this.data = new Slot[jClass.getJFields().length];
    }

    public JObject(JClass jClass, int size) {
        this.jClass = jClass;
        this.data = new Slot[size];
    }

    public JObject(JClass jClass, Object data) {
        this.jClass = jClass;
        this.data = data;
        if (data != null) {
            this.hashCode = this.data.hashCode();
        }
    }


    public static JObject transformToJObject(JClassLoader loader, Object val) throws ClassNotFoundException {
        JObject jObject = null;
        if (val != null) {
            if (val instanceof JObject) {
                return (JObject) val;
            }
            String className = val.getClass().getName().replaceAll("\\.", "/");
            JClass jc = null;
            try {
                jc = loader.loadJClass(className);
            } catch (IOException e) {
                e.printStackTrace();
            }
            jObject = jc.newJObject();
        }
        return jObject;
    }

    public static JObject newJArray(JClass jClass, int size) {
        switch (jClass.getClassName()) {
            case "[I":
                return new JObject(jClass, new int[size]);
            case "[S":
                return new JObject(jClass, new short[size]);
            case "[Z":
                return new JObject(jClass, new boolean[size]);
            case "[B":
                return new JObject(jClass, new byte[size]);
            case "[C":
                return new JObject(jClass, new char[size]);
            case "[J":
                return new JObject(jClass, new long[size]);
            case "[F":
                return new JObject(jClass, new float[size]);
            case "[D":
                return new JObject(jClass, new double[size]);
            default: {
                if (jClass.getClassName().equals("java/lang/String")) {
                    return new JString(jClass, new JString[size]);
                }
                return new JObject(jClass, new JObject[size]);
            }
        }
    }

    /**
     * @return
     */
    public Slot[] getFields() {
        return (Slot[]) data;
    }

    public void setFields(Slot[] slots) {
        this.data = slots;
    }

    public <T extends Slot> T getField(int index) {
        return (T) ((Slot[]) data)[index];
    }

    @Override
    public JObject clone() throws CloneNotSupportedException {
        return (JObject) super.clone();
    }

    @Override
    public boolean equals(Object o) {
        JObject o2 = (JObject) o;
        if (o2 instanceof JString) {
            JString os = (JString) o2;
            return os.equals(this);
        }
        if (this instanceof JString) {
            JString os = (JString) this;
            return os.equals(o);
        }
        return false;
    }
}
